package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.UserCommissionOrderDTO;
import info.batcloud.fanli.core.entity.AllotTypeCommissionFee;
import info.batcloud.fanli.core.entity.CommissionOrder;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.entity.UserCommissionOrder;
import info.batcloud.fanli.core.enums.CommissionOrderStatus;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.AllotTypeCommissionFeeRepository;
import info.batcloud.fanli.core.repository.UserCommissionOrderRepository;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.CommissionService;
import info.batcloud.fanli.core.service.TmpFileService;
import info.batcloud.fanli.core.service.UserCommissionOrderService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserCommissionOrderServiceImpl implements UserCommissionOrderService {

    @Inject
    private UserCommissionOrderRepository userCommissionOrderRepository;

    @Inject
    private AllotTypeCommissionFeeRepository allotTypeCommissionFeeRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private CommissionService commissionService;

    @Override
    public long findUserCommissionOrderNumByDay(long userId, Date day) {
        Date startTime = DateUtils.truncate(day, Calendar.DATE);
        Date endTime = DateUtils.addDays(startTime, 1);
        return userCommissionOrderRepository.countByUserIdAndCreateTimeBetween(userId, startTime, endTime);
    }

    @Override
    public float findUserEstimateCommissionFeeByDay(long userId, Date day) {
        Date startTime = DateUtils.truncate(day, Calendar.DATE);
        Date endTime = DateUtils.addDays(startTime, 1);
        Float val = userCommissionOrderRepository.findUserEstimateCommissionFeeByCreateTimeBetween(userId, startTime, endTime);
        return val == null ? 0 : val;
    }

    @Override
    public float findUserEstimateCommissionFeeByStartTime(long userId, Date startTime) {
        Float val = userCommissionOrderRepository.findUserEstimateCommissionFeeByCreateTimeIsAfter(userId, startTime);
        return val == null ? 0 : val;
    }

    @Override
    public float findUserEstimateCommissionFeeBetweenTime(long userId, Date startTime, Date endTime) {
        Float val = userCommissionOrderRepository.findUserEstimateCommissionFeeByCreateTimeBetween(userId, startTime, endTime);
        return val == null ? 0 : val;
    }

    @Override
    public float findUserSettledCommissionFeeBetweenTime(long userId, Date startTime, Date endTime) {
        Float val = userCommissionOrderRepository.findUserSettledCommissionFeeBySettledTimeBetween(userId, startTime, endTime);
        return val == null ? 0 : val;
    }

    @Override
    public float findUserPaidEstimateCommissionFee(long userId) {
        Float val = userCommissionOrderRepository.findUserPaidEstimateCommissionFee(userId);
        return val == null ? 0 : val;
    }

    @Override
    public float findUserWaitSettleEstimateCommissionFee(long userId) {
        Float val = userCommissionOrderRepository.findUserWaitSettleEstimateCommissionFee(userId);
        return val == null ? 0 : val;
    }

    @Override
    public float findUserUnSettleEstimateCommissionFee(long userId) {
        Float val = userCommissionOrderRepository.findUserUnSettleEstimateCommissionFee(userId);
        return val == null ? 0 : val;
    }

    @Override
    public List<AllotTypeCommissionFee> statUserEstimateAllotTypeCommissionFeeBetweenTime(long userId, Date startTime, Date endTime) {
        return allotTypeCommissionFeeRepository.statUserEstimateCommissionFeeByCreateTimeBetweenGroupByAllotType(userId, startTime, endTime);
    }

    @Override
    public Paging<UserCommissionOrderDTO> search(SearchParam param) {
        Specification<UserCommissionOrder> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getUserId() != null) {
                expressions.add(cb.equal(root.get("userId"), param.getUserId()));
            }
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("commissionOrder").get("status"), param.getStatus()));
            } else {
                expressions.add(cb.notEqual(root.get("commissionOrder").get("status"), CommissionOrderStatus.INVALID));
            }
            if (param.getCommissionOrderId() != null) {
                expressions.add(cb.equal(root.get("commissionOrder").get("id"), param.getCommissionOrderId()));
            }
            if (param.getCommissionItemId() != null) {
                expressions.add(cb.equal(root.get("commissionOrder").get("commissionItemId"), param.getCommissionItemId()));
            }
            if (param.getEcomPlat() != null) {
                expressions.add(cb.equal(root.get("commissionOrder").get("ecomPlat"), param.getEcomPlat()));
            }
            if (StringUtils.isNotBlank(param.getOrderNo())) {
                expressions.add(cb.equal(root.get("commissionOrder").get("orderNo"), param.getOrderNo()));
            }
            if (param.getMinCreateTime() != null) {
                expressions.add(cb.greaterThanOrEqualTo(root.get("commissionOrder").get("createTime"), param.getMinCreateTime()));
            }
            if (param.getMaxCreateTime() != null) {
                expressions.add(cb.lessThanOrEqualTo(root.get("commissionOrder").get("createTime"), param.getMaxCreateTime()));
            }
            if (param.getMinSettledTime() != null) {
                expressions.add(cb.greaterThanOrEqualTo(root.get("commissionOrder").get("settledTime"), param.getMinSettledTime()));
            }
            if (param.getMaxSettledTime() != null) {
                expressions.add(cb.lessThanOrEqualTo(root.get("commissionOrder").get("settledTime"), param.getMaxSettledTime()));
            }
            if (param.getItemId() != null) {
                expressions.add(cb.equal(root.get("commissionOrder").get("itemId"), param.getItemId()));
            }
            if (StringUtils.isNotBlank(param.getItemTitle())) {
                expressions.add(cb.like(root.get("commissionOrder").get("itemTitle"), "%" + param.getItemTitle() + "%"));
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "commissionOrder.createTime");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<UserCommissionOrder> page = userCommissionOrderRepository.findAll(specification, pageable);
        return PagingHelper.of(toBOList(page.getContent()), page.getTotalElements(),
                param.getPage(), param.getPageSize());
    }

    private static String[] TITLE_LIST = new String[]{
            "商品ID",
            "商品名称",
            "会员ID",
            "用户昵称",
            "手机号",
            "平台",
            "金额",
            "预估",
            "预估比",
            "佣金",
            "佣金比",
            "分配比",
            "预估佣金",
            "预估奖励",
            "结算总额",
            "结算佣金",
            "结算奖励",
            "分佣类型",
            "创建时间",
            "结算时间",
            "订单状态"
    };

    @Inject
    private TmpFileService tmpFileService;

    @Override
    public File exportXls(ExportParam param) throws IOException {
        File excelFile = tmpFileService.createFile("user_order_" + System.currentTimeMillis() + ".xls");
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet();
        sheet.autoSizeColumn(1, true);
        HSSFFont boldFond = workbook.createFont();
        boldFond.setBold(true);
        boldFond.setFontHeightInPoints((short) 16);
        CellStyle foldStyle = workbook.createCellStyle();
        CellStyle commonStyle = workbook.createCellStyle();
        HSSFFont commonFont = workbook.createFont();
        commonFont.setFontHeightInPoints((short) 14);
        commonStyle.setFont(commonFont);
        foldStyle.setFont(boldFond);
        HSSFRow titleRow = sheet.createRow(0);
        for (int i = 0; i < TITLE_LIST.length; i++) {
            HSSFCell titleCell = titleRow.createCell(i);
            //给单元格设置内容
            titleCell.setCellValue(TITLE_LIST[i]);
            titleCell.setCellStyle(foldStyle);
        }
        int page = 1;
        int pageSize = param.getMaxCount() < 100 ? param.getMaxCount() : 100;
        int maxPage = (param.getMaxCount() + pageSize - 1) / pageSize;
        int rowIndex = 1;
        param.setPageSize(pageSize);
        while (true) {
            param.setPage(page);
            Paging<UserCommissionOrderDTO> paging = this.search(param);
            for (UserCommissionOrderDTO dto : paging.getResults()) {
                HSSFRow row = sheet.createRow(rowIndex++);
                row.setHeightInPoints(20);
                for (int i = 0; i < TITLE_LIST.length; i++) {
                    HSSFCell rowCell = row.createCell(i);
                    rowCell.setCellStyle(commonStyle);
                    switch (TITLE_LIST[i]) {
                        case "商品ID":
                            rowCell.setCellValue(dto.getItemId());
                            break;
                        case "商品名称":
                            rowCell.setCellValue(dto.getItemTitle());
                            break;
                        case "会员ID":
                            rowCell.setCellValue(dto.getUserId());
                            break;
                        case "用户昵称":
                            if (dto.getUserNickname() != null) {
                                rowCell.setCellValue(dto.getUserNickname());
                            }
                            break;
                        case "手机号":
                            if (dto.getUserPhone() != null) {
                                rowCell.setCellValue(dto.getUserPhone());
                            }
                            break;
                        case "平台":
                            rowCell.setCellValue(dto.getEcomPlatTitle());
                            break;
                        case "金额":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getPayFee())));
                            break;
                        case "预估":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getCommissionOrderEstimateCommissionRate())));
                            break;
                        case "预估比":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(dto.getCommissionOrderEstimateCommissionRate());
                            break;
                        case "佣金":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getCommissionOrderCommissionFee())));
                            break;
                        case "佣金比":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getCommissionRate())));
                            break;
                        case "分配比":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getAllotCommissionRate())));
                            break;
                        case "预估佣金":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getEstimateCommissionFee())));
                            break;
                        case "预估奖励":
                            rowCell.setCellType(CellType.NUMERIC);
                            rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getEstimateRewardFee())));
                            break;
                        case "结算总额":
                            if (dto.getSettledFee() != null) {
                                rowCell.setCellType(CellType.NUMERIC);
                                rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getSettledFee())));
                            }
                            break;
                        case "结算佣金":
                            if (dto.getSettledCommissionFee() != null) {
                                rowCell.setCellType(CellType.NUMERIC);
                                rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getSettledCommissionFee())));
                            }
                            break;
                        case "结算奖励":
                            if (dto.getSettledRewardFee() != null) {
                                rowCell.setCellType(CellType.NUMERIC);
                                rowCell.setCellValue(String.valueOf(String.format("%.2f", dto.getSettledRewardFee())));
                            }
                            break;
                        case "分佣类型":
                            rowCell.setCellValue(dto.getAllotTypeTitle());
                            break;
                        case "创建时间":
                            rowCell.setCellValue(DateFormatUtils.format(dto.getCreateTime(), "yyyy-MM-dd HH:mm:ss"));
                            break;
                        case "结算时间":
                            if (dto.getSettledTime() != null) {
                                rowCell.setCellValue(DateFormatUtils.format(dto.getSettledTime(), "yyyy-MM-dd HH:mm:ss"));
                            }
                            break;
                        case "订单状态":
                            rowCell.setCellValue(dto.getStatusTitle());
                            break;
                    }
                }
            }
            if (!paging.getHasNext()) {
                break;
            }
            if (page >= maxPage) {
                break;
            }
            page++;
        }
        for (int i = 0; i < TITLE_LIST.length; i++) {
            sheet.autoSizeColumn(i);
        }
        workbook.write(excelFile);
        return excelFile;
    }

    private List<UserCommissionOrderDTO> toBOList(List<UserCommissionOrder> commissionOrderList) {
        List<UserCommissionOrderDTO> commissionOrderBOList = new ArrayList<>();
        List<Long> userIdList = commissionOrderList.stream().map(c -> c.getUserId()).filter(id -> id != null).collect(Collectors.toList());
        List<User> userList = userRepository.findByIdIn(userIdList);
        Map<Long, User> userMap = new HashMap<>();
        for (User user : userList) {
            userMap.put(user.getId(), user);
        }

        for (UserCommissionOrder uco : commissionOrderList) {
            UserCommissionOrderDTO bo = new UserCommissionOrderDTO();
            bo.setUserId(uco.getUserId());
            bo.setId(uco.getId());
            CommissionOrder co = uco.getCommissionOrder();
            bo.setItemId(co.getItemId());
            bo.setSettledFee(uco.getSettledFee());
            bo.setEstimateCommissionFee(uco.getCommissionRate() * co.getEstimateCommissionFee() / 100);
            bo.setEcomPlat(co.getEcomPlat());
            bo.setCommissionRate(uco.getCommissionRate() * co.getEstimateCommissionRate() / 100);
            bo.setCreateTime(co.getCreateTime());
            bo.setAllotCommissionRate(uco.getCommissionRate());
            bo.setItemPicUrl(co.getItemPicUrl());
            bo.setItemTitle(co.getItemTitle());
            bo.setShopTitle(co.getShopTitle());
            bo.setPayFee(co.getPayFee());
            bo.setAllotType(uco.getAllotType());
            bo.setStatus(co.getStatus());
            bo.setSettledTime(uco.getSettledTime());
            bo.setCommissionOrderEstimateCommissionFee(co.getEstimateCommissionFee());
            bo.setCommissionOrderEstimateCommissionRate(co.getEstimateCommissionRate());
            bo.setCommissionOrderCommissionRate(co.getCommissionRate());
            bo.setCommissionOrderCommissionFee(co.getCommissionFee());
            bo.setCommissionItemId(co.getCommissionItemId());
            bo.setCommissionRewardRate(uco.getCommissionRewardRate());
            bo.setEstimateRewardFee(commissionService.determineRewardCommissionFee(uco.getCommissionRewardRate(), bo.getEstimateCommissionFee()));
            bo.setSettledCommissionFee(uco.getSettledCommissionFee());
            bo.setSettledRewardFee(uco.getSettledRewardFee());
            User user = userMap.get(bo.getUserId());
            if (user != null) {
                bo.setUserAvatarUrl(user.getAvatarUrl());
                bo.setUserNickname(user.getNickname());
                bo.setUserPhone(user.getPhone());
            }
            commissionOrderBOList.add(bo);
        }
        return commissionOrderBOList;
    }

}
